home *** CD-ROM | disk | FTP | other *** search
/ Atari Mega Archive 1 / Atari Mega Archive - Volume 1.iso / music / midiplay.lzh / MP_INTP.C < prev    next >
C/C++ Source or Header  |  1991-11-30  |  16KB  |  613 lines

  1. /*
  2.  * File: mp_intp.c
  3.  * SGoldthorpe    20-Jul-91
  4.  */
  5.  
  6. /*
  7.  * mp_intp - the midi file interpreter for midiplay
  8.  * This software is (C) 1991 Stephen Goldthorpe but it's FREE!  Usual
  9.  * disclaimers and notices about this software not being sold for profit.
  10.  * But you may take all you want from the code though!  If you have any
  11.  * suggestions/bug fixes please get in contact with me.  I don't want to
  12.  * maintain code i've never even seen before (life's hard enough without all
  13.  * of that)!
  14.  *                        -Steve Goldthorpe
  15.  * Phone (DAYTIME UK):    +44 707 382350
  16.  * Internet E-Mail:    SGoldthorpe.wgc-e@rx.xerox.com
  17.  *            goldthor@arisia.xerox.com
  18.  *
  19.  * Version 0.5 by Piet van Oostrum <piet@cs.ruu.nl>
  20.  * November 1991.
  21.  * I made the following changes:
  22.  * 1. Files > 32767 wouldn't play. I have changed a couple of ints to
  23.  *    LONG. Also malloc'ed the buffer with a variable rather than a fixed size.
  24.  * 2. replaced array referenced with a[i] rather than *(a+i). I find this
  25.  *    more readable but it has the same meaning.
  26.  * 3. Midifiles of more than about 3 minutes didn't play right. I think
  27.  *    this is a bug in ltod (long to double) in the floating point lib. I
  28.  *    changed the timing from using floating point arithmetic to LONG
  29.  *    arithmetic.
  30.  *    Anyway the timing was not robust in the presence of tempo changes
  31.  *    because the new time per beat would be applied to the time from the
  32.  *    beginning of the piece rather than from the time of the tempo change.
  33.  * 4. I introduced the -T option to send timing commands. This can be
  34.  *    used to trigger a drum computer or an arranger.
  35.  *
  36.  * NOTE: I distribute this version with the consent of Steve
  37.  * Goldthorpe. I think he should not be bothered with bugs in my version!
  38.  */
  39.  
  40. #include    <stdio.h>
  41. #include    <types.h>
  42. #include    <time.h>
  43. #include    <string.h>
  44.  
  45. /* and for the atari OS stuff */
  46. #include    <osbind.h>
  47.  
  48. /* #include    "midiplay.h" included by mp_gbls.h */
  49. #include    "mp_gbls.h"
  50.  
  51. /* GLOBAL VARIABLES */
  52. /* The track_delta's are in units of 1/division clockticks */
  53. /* time_per_beat is the length of a quarter note in clock ticks */
  54.    
  55. static long    track_delta[MAX_TRACKS], division, time_per_beat;
  56. static BYTE    *track_pos[MAX_TRACKS];
  57. static long    track_left[MAX_TRACKS];
  58. static int    track_finished[MAX_TRACKS];
  59. static clock_t    clock_orig;
  60. static char    *gFile;
  61. static WORD    format, tracks;
  62. static int    finished_tracks;
  63. static int    trnr;
  64. static long    clock_delta, clock_time;
  65.  
  66. /* FUNCTION DECLS */
  67. BOOL        interp();
  68. static void    truncated(), all_notes_off(), parse_error();
  69.  
  70. /* MACRO FUNCTIONS */
  71. /* the error checking may be a bit OTT but I'm gonna do it anyway  (helps
  72.    catch those naughty bugs - and bad files) */
  73. #define GET32BITS(dw,p,l)    dw=(((LONG)(*p)<<24)+            \
  74.                     ((LONG)(*(p+1))<<16)+        \
  75.                     (((LONG)*(p+2))<<8)+        \
  76.                     (LONG)(*(p+3)));        \
  77.                 if(l<4)                    \
  78.                 {    truncated();            \
  79.                     return(FALSE);            \
  80.                 };                    \
  81.                 p += 4; l -= 4
  82.  
  83. #define    GET16BITS(w,p,l)    w=(((WORD)(*p)<<8)+(WORD)*(p+1));    \
  84.                 if(l<2)                    \
  85.                 {    truncated();            \
  86.                     return(FALSE);            \
  87.                 }                    \
  88.                 p += 2; l -= 2
  89.  
  90. #define    GET8BITS(b,p,l)        b = *(p)++;                \
  91.                 if(--l<0)                \
  92.                 {    truncated();            \
  93.                     return(FALSE);            \
  94.                 }
  95.  
  96. #define    GETVARLEN(dw,p,l)    for(dw=(LONG)(*p)&0x7f;(*(p)++)&0x80;)    \
  97.                 {    if(--l<0)            \
  98.                     {    truncated();        \
  99.                         return(FALSE);        \
  100.                     };                \
  101.                     dw <<=7;            \
  102.                     dw |= (LONG)(*p)&0x7f;        \
  103.                 };                    \
  104.                 if(--l<0)                \
  105.                 {    truncated();            \
  106.                     return(FALSE);            \
  107.                 }
  108.  
  109. #define    CHECKLEFT(l,v)        if(l<v)                    \
  110.                 {    truncated();            \
  111.                     return(FALSE);            \
  112.                 }
  113.  
  114. #define    SEND(b)            Bconout(3,b)
  115.  
  116. /* FUNCTION DEFS */
  117. BOOL        interp(buffer, file, len)
  118. char        *file;
  119. BYTE        *buffer;
  120. LONG        len;
  121. {   BYTE    *pos=buffer,*next;
  122.     BYTE    running_status[MAX_TRACKS],last_running_status;
  123.     WORD    w;
  124.     LONG    dw,delta;
  125.     LONG    left=len;
  126.  
  127.     gFile=file;
  128.  
  129.     /* check header */
  130.     if((left<4)||(strncmp("MThd",(char*)pos,4)!=0))
  131.     {    (void)fprintf(stderr,"%s: %s is not a midi file\n",app_name,file);
  132.     return(FALSE);
  133.     };
  134.     pos += 4; left -= 4;
  135.  
  136.     /* find address of next chunk */
  137.     GET32BITS(dw,pos,left);
  138.     next=pos+dw;
  139. #ifdef DEBUG
  140.     (void)printf("pos is %lx len is %ld next would be %lx\n",pos-buffer,dw,
  141.     next-buffer);
  142. #endif
  143.  
  144.     /* get file format */
  145.     GET16BITS(format,pos,left);
  146.     switch (format)
  147.     {    /* OK we accept formats 0 and 1 */
  148.     case 0:
  149.     case 1:
  150.              break;
  151.     /* but we don't do any others */
  152.     default:
  153.     (void)fprintf(stderr,"%s: can't play %s, midi file type %d\n",app_name,
  154.         file,format);
  155.     return(FALSE);
  156.     };
  157.  
  158.     /* get number of tracks */
  159.     GET16BITS(tracks,pos,left);
  160.  
  161.     /* check tracks in range */
  162.     if(tracks > MAX_TRACKS)
  163.     {    (void)fprintf(stderr,"%s: %s has too many tracks (%d allowed).\n",
  164.         app_name,file,tracks,MAX_TRACKS);
  165.     return(FALSE);
  166.     };
  167.  
  168.     /* get division - this is the division of a quarter note or if negative is
  169.        frame based. */
  170.     GET16BITS(w,pos,left);
  171.     division=(LONG)w;
  172.  
  173.     /* I don't suport the frame stuff yet! */
  174.     if(division < 0)
  175.     {    (void)fprintf(stderr,
  176.         "%s: file %s - don't support framed based files yet!", app_name,
  177.         file);
  178.     return(FALSE);
  179.     };
  180. #ifdef DEBUG
  181.     (void)printf("division = %f\n",division);
  182. #endif
  183.  
  184.     time_per_beat = (LONG)(CLK_TCK/2); /* default 120bpm = 2bps */
  185. #ifdef DEBUG
  186.     (void)printf("time per beat (1/%d sec) %ld\n", CLK_TCK, time_per_beat);
  187. #endif
  188.  
  189.     /* do some initialisation, track finding etc */
  190.     finished_tracks = 0;
  191.     for(trnr=0;trnr<tracks;trnr++)
  192.     {    CHECKLEFT(left,(next-pos));
  193.     left -= next-pos; pos = next;
  194.  
  195.     /* track start  */
  196.     if((left<4)||(strncmp("MTrk",(char*)pos,4)!=0))
  197.     {   (void)fprintf(stderr,"%s: %s parse error, track expected\n",
  198.         app_name,file);
  199. #ifdef DEBUG
  200.         (void)printf("posn %lx, bytes around (-3..3) %02x %02x %02x %02x \
  201. %02x %02x %02x\n",pos-buffer,*(pos-3),*(pos-2),*(pos-1),*pos,*(pos+1),*(pos+2),
  202.         *(pos+3));
  203. #endif
  204.         return(FALSE);
  205.     };
  206.     pos += 4; left -= 4;
  207.  
  208.     /* find address of next chunk */
  209.     GET32BITS(dw,pos,left);
  210.     track_pos[trnr] = pos;
  211.     next = pos+dw;
  212.     track_left[trnr] = dw;
  213. #ifdef DEBUG
  214.     (void)printf("pos is %lx len is %ld next would be %lx\n",pos-buffer,dw,
  215.         next-buffer);
  216. #endif
  217.  
  218.     /* get initial delta time */
  219.     GETVARLEN(dw,track_pos[trnr],track_left[trnr]);
  220.     track_delta[trnr]=dw * time_per_beat;
  221.     track_finished[trnr]=FALSE;
  222.     running_status[trnr] = 0xfe;
  223.     last_running_status = 0xfe;
  224.     };
  225.  
  226.     /* let the user know what's happening */
  227.     (void)printf("playing '%s' with %d %s\n",file,tracks,
  228.                             tracks==1?"track":"tracks");
  229.  
  230.     /* get start time */
  231.     clock_orig=clock();
  232.     clock_time=0;
  233.     if (f_Timing) SEND (0xFA);
  234.         
  235.     /* dispatcher */
  236.     while (finished_tracks != tracks)
  237.     {    BYTE    event;
  238.     clock_delta = (clock()-clock_orig)*division;
  239.     if (f_Timing && clock_delta >= clock_time) {
  240.         SEND(0xF8);
  241.         clock_time += (time_per_beat * division / 24);
  242.     }
  243.     for(trnr=0;trnr<tracks;trnr++)
  244.     {   if (!track_finished[trnr] & (clock_delta >= track_delta[trnr]))
  245.         {    GET8BITS(event,track_pos[trnr],track_left[trnr]);
  246.  
  247.         /* parse event */
  248.         switch (event)
  249.         {   /* meta-events */
  250.             case 0xff:
  251.             {    if(!meta_event())
  252.                 return(FALSE);
  253.             break;
  254.             };
  255.             /* sysex events */
  256.             case 0xf0:
  257.             case 0xf7:
  258.             {    if(!sysex_event(event))
  259.                 return(FALSE);
  260.             break;
  261.             };
  262.             case 0xf1:
  263.             case 0xf2:
  264.             case 0xf3:
  265.             case 0xf4:
  266.             case 0xf5:
  267.             case 0xf6:
  268.             {    if(!system_common(event))
  269.                 return(FALSE);
  270.             break;
  271.             };
  272.             case 0xf8:
  273.             case 0xf9:
  274.             case 0xfa:
  275.             case 0xfb:
  276.             case 0xfc:
  277.             case 0xfd:
  278.             case 0xfe:
  279.             {    if(!system_real_time())
  280.                 return(FALSE);
  281.             break;
  282.             };
  283.             /* midi events */
  284.             default:
  285.             {    switch(event & 0xf0)
  286.             {   /* 3 byte events */
  287.                 case 0x90:
  288.                 case 0x80:
  289.                 case 0xa0:
  290.                 case 0xb0:
  291.                 case 0xe0:
  292.                 {    BYTE    c;
  293.                 SEND(event);
  294.